#pragma once

#include <iostream>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cerrno>
#include <signal.h>
#include <stdlib.h>
#include <cstring>
#include <unistd.h>
#include <cassert>
#include <pthread.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string>
#include <sys/epoll.h>
#include <fcntl.h>

#define BUFFER_NUM 1024
#define MAX_EVENTS 1024

// if condition is false,exit!
void myAssert(bool condition, const char *errmsg)
{
    if (!condition)
    {
        perror(errmsg);
        exit(EXIT_FAILURE);
    }
}

// 设置非阻塞模式
void setnonblocking(int fd)
{
    int flag = fcntl(fd, F_GETFL, 0);
    flag |= O_NONBLOCK;
    fcntl(fd, F_SETFL, flag);
}

class Socket
{
public:
    Socket(std::string ip = "", uint16_t port = 8080)
        : _ip(ip), _port(port)
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        myAssert(_sockfd != -1, "sock error");
    }

    int GetFd() { return _sockfd; }

    void Bind()
    {
        struct sockaddr_in serv_addr;
        bzero(&serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = _ip == "" ? INADDR_ANY : inet_addr(_ip.c_str());
        serv_addr.sin_port = htons(_port);
        myAssert(bind(_sockfd, (sockaddr *)&serv_addr, sizeof(serv_addr)) != -1, "bind error");
    }

    void Listen()
    {
        myAssert(listen(_sockfd, SOMAXCONN) != -1, "listen error");
    }

    int Accept()
    {
        struct sockaddr_in cli_addr;
        bzero(&cli_addr, sizeof(cli_addr));
        socklen_t len = sizeof(cli_addr);
        int cli_fd = accept(_sockfd, (sockaddr *)&cli_addr, &len);
        myAssert(cli_fd != -1, "accept error");
        printf("new client connect client_fd: %d IP: %s port: %d\n",
               cli_fd, inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));

        return cli_fd;
    }
    void Connect()
    {
        struct sockaddr_in server_addr;
        bzero(&server_addr, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = inet_addr(_ip.c_str());
        server_addr.sin_port = htons(_port);
        connect(_sockfd, (sockaddr *)&server_addr, sizeof(server_addr));
    }
    ~Socket()
    {
        close(_sockfd);
    }

private:
    Socket(const Socket &) = delete;
    int _sockfd;
    std::string _ip;
    uint16_t _port;
};

class Epoll
{
public:
    // 初始化epfd和events
    Epoll()
    {
        _epfd = epoll_create1(0);
        bzero(events, sizeof(events));
    }
    // 向epoll里面加一个fd
    void addFd(int fd, uint32_t flag)
    {
        bzero(&ev, sizeof(ev));
        ev.data.fd = fd;
        ev.events = flag;
        setnonblocking(fd);
        epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &ev);
    }
    // 获取就绪fd个数
    int size()
    {
        return epoll_wait(_epfd, events, MAX_EVENTS, -1);
    }
    // 获取触发的fd具体是多少
    int triggerFd(int i)
    {
        return events[i].data.fd;
    }
    // 获取触发fd的事件
    uint32_t triggerThing(int i)
    {
        return events[i].events;
    }
    // void updateChannel(Channel* channel)
    // {
    //     int fd=channel->getFd();
    //     struct epoll_event ev;
    //     bzero(&ev,sizeof(ev));
    //     ev.data.ptr=channel;
    //     ev.events=channel->getEvents();
    //     if(!channel->getInpoll())
    //     {
    //         myAssert(epoll_ctl(_epfd,EPOLL_CTL_ADD,fd,&ev)!=-1,"epoll_ctl add error");
    //         channel->setInEpoll();
    //     }
    //     else
    //     {
    //         myAssert(epoll_ctl(_epfd,EPOLL_CTL_MOD,fd,&ev)!=-1,"epoll_ctl mod error");
    //     }
    // }
    ~Epoll() {}

private:
    Epoll(const Epoll &) = delete;
    int _epfd;
    struct epoll_event events[MAX_EVENTS];
    struct epoll_event ev;
};
//调查vector<channel>而不是vector<events>
//对某个fd上的
// class Channel
// {
// public:
//     Channel(Epoll* ep,int fd):_ep(ep),_fd(fd)
//     {}
//     void enableReading()
//     {
//         _events=EPOLLIN|EPOLLET;
//         _ep->updateChannel(this);
//     }
//     int getFd() { return _fd; }
//     uint32_t getEvents()
//     {
//         return _events;
//     }
//     ~Channel(){}
// private:
//     Epoll *_ep;
//     int _fd;
//     uint32_t _events;  // 监听这个文件描述符的事件
//     uint32_t _revents; // epoll返回时该channel时文件描述符正在发生的事情
//     bool _inEpoll;     // 该channel是否在epoll中
// };